<!--
 * @Descripttion: 级联选择器
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2024-02-29 11:07:08
 * @LastEditors: wamgmeng
 * @LastEditTime: 2024-05-27 13:13:17
-->
<template>
	<div class="b-cascaderSelect-wrap">
		<el-cascader
			ref="cascaderRef"
			:key="cascaderKey"
			v-model="cascaderValue"
			:props="cascaderProps"
			:placeholder="cascaderPlaceholder"
			clearable
			filterable
			:before-filter="beforeFilter"
			@change="handleChange"
			@clear="handleClear"
		></el-cascader>
	</div>
</template>

<script setup lang="ts">
import { ref, watch, nextTick, onMounted, onUpdated, onUnmounted } from 'vue';
import type { CascaderNode, Resolve } from 'element-plus/es/components/cascader-panel/src/types';
import { getRoomListAPI, getCampusListAPI, getBuildingListAPI } from '@/api/index';
import type { CascaderInstanceType, CascaderPropsType } from '@/api/index';

const emit = defineEmits<{
	(
		e: 'cascaderSelectData',
		obj: {
			campusCode: string;
			buildingCode: string;
			roomCode: string;
		}
	): void;
}>();

const props = withDefaults(
	defineProps<{
		cascaderPlaceholder: string;
		positionDefaultOption: string[];
	}>(),
	{
		cascaderPlaceholder: '',
		positionDefaultOption: () => []
	}
);

onUnmounted(() => {
	// 移除监听器
	if (currentInputEl) {
		currentInputEl.removeEventListener('input', handleInputClear);
	}
});

// el-cascader 实例
const cascaderRef = ref<CascaderInstanceType | null>(null);
// cascader强制更新
const cascaderKey = ref(0);
// cascader值
const cascaderValue = ref<string[]>([]);
// 搜索关键字
const keyWord = ref('');
// 保存输入框元素引用，避免重复绑定
let currentInputEl: HTMLInputElement | null = null;

// 输入框 input 事件处理函数
const handleInputClear = (e: Event) => {
	const target = e.target as HTMLInputElement;
	const inputValue = target.value;

	// 当输入框被清空时，重新加载组件
	if (!inputValue && keyWord.value) {
		refresh();
	}
};

// 绑定输入框事件的函数
const bindInputEvent = () => {
	nextTick(() => {
		const cascaderEl = document.querySelector('.b-cascaderSelect-wrap .el-cascader');
		if (cascaderEl) {
			const inputEl = cascaderEl.querySelector('input') as HTMLInputElement;
			// 如果找到了新的输入框元素
			if (inputEl && inputEl !== currentInputEl) {
				// 先移除旧元素的监听器
				if (currentInputEl) {
					currentInputEl.removeEventListener('input', handleInputClear);
				}
				// 绑定新元素的监听器
				inputEl.addEventListener('input', handleInputClear);
				// 更新当前输入框元素引用
				currentInputEl = inputEl;
			}
		}
	});
};

// 清空选择时重新加载组件
watch(
	cascaderValue,
	(newV, oldV) => {
		if (newV && !newV.length && oldV && oldV.length) {
			refresh();
		}
	},
	{ deep: true, immediate: true }
);
watch(
	() => props.positionDefaultOption,
	(val) => {
		if (val && val.length) {
			cascaderValue.value = JSON.parse(JSON.stringify(val));
		}
	}
);

// 组件挂载时绑定输入框事件
onMounted(() => {
	bindInputEvent();
});

// 组件更新时重新绑定输入框事件（当 cascaderKey 变化导致组件重新渲染时）
onUpdated(() => {
	bindInputEvent();
});

// 组件值变化的回调
const handleChange = (value: string[]): void => {
	cascaderRef.value && cascaderRef.value.togglePopperVisible();
	const obj = {
		campusCode: '',
		buildingCode: '',
		roomCode: ''
	};
	if (keyWord.value && value?.length) {
		const _value = value[0].split('_');
		obj.campusCode = _value[0];
		obj.buildingCode = _value[1];
		obj.roomCode = _value[2];
	} else if (!keyWord.value && value?.length) {
		if (value.length === 1) {
			obj.campusCode = value[0];
		} else if (value.length === 2) {
			obj.campusCode = value[0];
			obj.buildingCode = value[1];
		} else if (value.length === 3) {
			obj.campusCode = value[0];
			obj.buildingCode = value[1];
			obj.roomCode = value[2];
		}
	}
	emit('cascaderSelectData', obj);
};

// 懒加载数据
const lazyLoad = (node: CascaderNode, resolve: Resolve) => {
	const { level } = node;
	if (!node.data || !node.data.value) {
		// 如果是搜索
		if (keyWord.value) {
			getRoomListAPI({ name: keyWord.value }).then((data) => {
				const nodes = Array.from(data.list).map((item) => {
					return {
						value: `${item.campusCode}_${item.buildingCode}_${item.code}`,
						label: `${item.campusName} / ${item.buildingName} / ${item.name}`,
						leaf: true
					};
				});

				// 将搜索关键词回显/
				nextTick(() => {
					// Element Plus 中需要通过 DOM 查询获取输入框
					const cascaderEl = document.querySelector('.b-cascaderSelect-wrap .el-cascader');
					if (cascaderEl) {
						const inputEl = cascaderEl.querySelector('input');
						if (inputEl) {
							inputEl.value = keyWord.value;
							// 输入框聚焦
							inputEl.focus();
						}
					}
				});
				// 打开下拉面板显示搜索结果
				cascaderRef.value && cascaderRef.value.togglePopperVisible();
				resolve(nodes);
			});
		} else {
			getCampusListAPI().then((data) => {
				const nodes = Array.from(data.list).map((item) => {
					return {
						value: item.code,
						label: `${item.name}`,
						leaf: level >= 1
					};
				});
				resolve(nodes);
			});
		}
	}
	if (node.level === 1) {
		getBuildingListAPI({
			campusCode: String(node.data.value)
		}).then((data) => {
			const nodes = Array.from(data.list).map((item) => {
				return {
					value: item.code,
					label: `${item.name}`,
					leaf: level >= 2
				};
			});
			resolve(nodes);
		});
	}
	if (node.level === 2) {
		getRoomListAPI({
			buildingCode: String(node.data.value)
		}).then((data) => {
			const nodes = Array.from(data.list).map((item) => {
				return {
					value: item.code,
					label: `${item.name}`,
					leaf: level >= 2
				};
			});
			resolve(nodes);
		});
	}
	if (node.level === 3) {
		resolve([]);
	}
};

const cascaderProps: CascaderPropsType = {
	lazy: true,
	lazyLoad,
	checkStrictly: true
};

// 筛选之前的钩子
const beforeFilter = (value: string): boolean => {
	keyWord.value = value;
	// 只有当有值时才重新渲染
	if (value) {
		nextTick(() => {
			cascaderKey.value += 1;
		});
	}
	// 停止组件默认的筛选
	return false;
};

// 重新加载组件
const refresh = (): void => {
	// 清空输入
	keyWord.value = '';
	// 重新渲染组件
	cascaderKey.value += 1;
	// 展开下拉面板
	nextTick(() => {
		cascaderRef.value && cascaderRef.value.togglePopperVisible();
	});
};

// 点击清空的回调
const handleClear = () => {
	if (keyWord.value) {
		cascaderValue.value = [];
		emit('cascaderSelectData', {
			campusCode: '',
			buildingCode: '',
			roomCode: ''
		});
	}
};
</script>

<style lang="scss">
// 联级选择器最后一级 不显示radio 同时点击文字可以选中
.el-cascader-panel {
	.el-cascader-menu:nth-child(3) {
		.el-cascader-node {
			.el-radio {
				width: 100%;
				position: absolute;
				opacity: 0;
			}
		}
	}
}
</style>
